#include "xEntity3D.h"
#include "xMeshManager.h"
#include "xMesh.h"
#include "xLogManager.h"
#include "xMaterial.h"
#include "xTechnique.h"


namespace XE
{

	const  xString xEntity3D::RenderType="Enity3D";
	xEntity3D::xEntity3D(const  xString& name,xSceneManager* pSceneMrg,const xString& MeshName)
		:xRenderObject(name,pSceneMrg),m_pAniationSetCollect(NULL)
	{
		m_pMesh = xMeshManager::getSingleton().GetByName(MeshName);
		//m_pMaterial=static_cast<xMaterial*>(xMaterialManager::getSingleton().GetByName(MaterialName));

		//UpDateBox();
		BuildMeshMaterial();

	}


	xEntity3D::~xEntity3D()
	{
		SafeDelete(m_pAniationSetCollect)

	}

	HRESULT xEntity3D::RenderCallBack(XRenderSystem* pRender,xCamera* pCamera,xRenderEntry* pEntry,const u32Flags* RenderFlag)
	{
/*
		//if(FAILED (xRenderObject::Render(pRender,pCamera)))
		//	return X_FAIL;

		//if(m_pMesh.isNull())
		//	return X_OK;
        
		
		//if(m_pAniationSetCollect!=NULL)
		//	m_pAniationSetCollect->AddTime(150.0f);

		//int bone  = 0;
		//if(m_pSkeleton.isNull()==false&&m_pSkeleton->GetRootBone()!=NULL&&m_pMesh->GetSkin()!=NULL)
		//{
		//	//return X_OK;
		//	//m_pSkeleton->GetRootBone()->update(false,bone);
		//	m_pSkeleton->UpDataAllBone(m_pMesh->GetSkin());
		//	pRender->SetBoneMatrixArray(m_pSkeleton->GetBoneTransMatrix(),m_pSkeleton->GetBoneCount());

		//}

		xRenderScheme* pTech =pEntry->m_pRenderScheme;
        if(pTech->GetTexutreNumber()==0)
		{

			pRender->SetTexture(0,NULL);

		}else 
		{
			for(int i = 0;i<pTech->GetTexutreNumber();++i)
			{
				pRender->SetTexture(i,pTech->GetTexture(i).getPointer());
			}

		}

		xNode* ptemnode=NULL;
		if(m_pAttachNode!=NULL)
		{
			ptemnode=m_pAttachNode;

		}
		if(ptemnode==NULL)
		{
			ptemnode=m_pParentNode;
		}
		if(ptemnode==NULL)
			return X_FAIL;



		xShaderPtr pShader = xShaderManager::getSingleton().GetByHandle(pEntry->m_ShaderHandle);
		if(RenderFlag.testBit(Active_Technique)||RenderFlag.testBit(Active_Shader))
		{


			UINT cc = 0;
		//	pShader->Begin(cc,0);
	//		pShader->BeginPass(pEntry->m_PassIndex);

		}else if(RenderFlag.testBit(Active_Pass))
		{
	//		pShader->BeginPass(pEntry->m_PassIndex);
		}

       if(pEntry->m_pRenderScheme->UpdateParameter(pRender)==false)
		{
			return X_FAIL;
		}*/
	//	pShader->CommitChanges();
	
		return	 m_pMesh->DrawSubMesh(pEntry->m_useData);

	/*	if(RenderFlag.testBit(Active_Pass)||RenderFlag.testBit(Active_Technique)||RenderFlag.testBit(Active_Shader))
		{
			pShader->EndPass();
		}*/

	/*	if(RenderFlag.testBit(Active_Technique)||RenderFlag.testBit(Active_Shader))
		{
			pShader->End();
		}
 	*/

	}

/*

	HRESULT xEntity3D::Render(XRenderSystem* pRender,xCamera* pCamera)
	{
		if(FAILED (xRenderObject::Render(pRender,pCamera)))
			return X_FAIL;

		return X_OK;
	
	   //xMaterial* pUserMaterial = m_pMaterial;
	 //  if(m_pMaterial==NULL)
	  // {
	//	   pUserMaterial =static_cast<xMaterial*>(xMaterialManager::getSingleton().GetByName(DefaultMaterial));
	//	   if(pUserMaterial==NULL)
	//		   return X_FAIL;

	 //  }

		if(m_pMesh.isNull())
			return X_OK;

		if(m_pAniationSetCollect!=NULL)
			m_pAniationSetCollect->AddTime(150.0f);

		int bone  = 0;
		if(m_pSkeleton.isNull()==false&&m_pSkeleton->GetRootBone()!=NULL&&m_pMesh->GetSkin()!=NULL)
		{
			//m_pSkeleton->GetRootBone()->update(false,bone);
			m_pSkeleton->UpDataAllBone(m_pMesh->GetSkin());
			pRender->SetBoneMatrixArray(m_pSkeleton->GetBoneTransMatrix(),m_pSkeleton->GetBoneCount());

		}
        
		

		xNode* ptemnode=NULL;
		if(m_pAttachNode!=NULL)
		{
			ptemnode=m_pAttachNode;

		}
		if(ptemnode==NULL)
		{
			ptemnode=m_pParentNode;
		}
		if(ptemnode==NULL)
			return X_FAIL;




		float dis  = pCamera->GetDistance(m_pAttachNode);
		m_pMesh->Render(pRender,dis);

		return X_OK;
	}
*/

	//void   xEntity3D::UpDateBox()
	//{
	//     if(m_pMesh.isNull())
	//	 {
 //          m_Box.m_min.x=0.0f;m_Box.m_min.y=0.0f;m_Box.m_min.z=0.0f;
 //          m_Box.m_max.x=0.0f;m_Box.m_max.y=0.0f;m_Box.m_max.z=0.0f;
	//	   m_Radius = 0.0f;
	//	   return ;
	//	 }
	//	 m_Box = *(m_pMesh->GetLoaclBox());
	//	 m_Radius = m_pMesh->GetRadius();

	//}


	void xEntity3D::SetMaterial(UINT MateerialID,xMaterialPtr	 pMaterial)
	{
		if(m_MaterialVertex.size()<MateerialID+1)
		{
			m_MaterialVertex.resize(MateerialID+1);
		}
		m_MaterialVertex[MateerialID ]= pMaterial;
		BuildMeshMaterial();
	}

	void xEntity3D::SetMaterial(UINT MateerialID,const xString& name)
	{
		if(m_MaterialVertex.size()<MateerialID+1)
		{
			m_MaterialVertex.resize(MateerialID+1);
		}

		xMaterialPtr pMaterial =xMaterialManager::getSingleton().GetByName(name);
		m_MaterialVertex[MateerialID ]= pMaterial;
		BuildMeshMaterial();
	}


	bool xEntity3D::SetMesh(xMeshPtr pMesh)
	{
		if(pMesh==m_pMesh)
			return true;
		m_pMesh = pMesh;
		updataSkeletonAndAnimationset();
		BuildMeshMaterial();
		if(m_pAttachNode!=NULL)
			m_pAttachNode->_updateBuildBox();
		return true;
	}

	void  xEntity3D::BuildMeshMaterial()
	{
       if(m_pMesh.isNull())
		   return ;
	   UINT subCount = m_pMesh->GetSubMeshCount();
	   subCount = min(subCount,m_MaterialVertex.size());
	   for(size_t i = 0,count = m_MaterialVertex.size();i<count;++i)
	   {
		   m_pMesh->SetSubMeshMaterial((UINT)i,m_MaterialVertex.at(i));
	   }
	   return ;
	}

	xMaterialPtr  xEntity3D::GetMaterial(UINT MaterialID)const
	{
		if(MaterialID>m_MaterialVertex.size()-1)
			return xMaterialPtr();
		return m_MaterialVertex.at(MaterialID);
	}



	bool xEntity3D::SetMesh(const xString& name)
	{
	    xMeshPtr	 pMesh = xMeshManager::getSingleton().GetByName(name);
		return SetMesh(pMesh);
	}

	void xEntity3D::SetSkeleton(xSkeletonPtr pSkeletion)
	{
		m_pSkeleton =pSkeletion;
		updataSkeletonAndAnimationset();
	}


	 void  xEntity3D::_updateRenderQueue(xRenderQueue* pRenderQueue,const xCamera* pCamera)
	 {
		// xRenderObject::_updateRenderQueue(pRenderQueue);

		 //--------update all bone-----------
		 //if(m_pSkeleton.isNull()==false&&m_pMesh.isNull()==false)
		 //{
			// m_pSkeleton->UpDataAllBone(m_pMesh->GetSkin());

		 //}



		 if(m_pAniationSetCollect!=NULL)
			 m_pAniationSetCollect->AddTime(150.0f);

		 int bone  = 0;
		 if(m_pSkeleton.isNull()==false&&m_pSkeleton->GetRootBone()!=NULL&&m_pMesh->GetSkin()!=NULL)
		 {
			 //return X_OK;
			 //m_pSkeleton->GetRootBone()->update(false,bone);
			 m_pSkeleton->UpDataAllBone(m_pMesh->GetSkin());
			// pRender->SetBoneMatrixArray(m_pSkeleton->GetBoneTransMatrix(),m_pSkeleton->GetBoneCount());

		 }


		const xMatrix* pMat = GetWorldMatrix();
		xVector3 disvec =  pCamera->GetWorldPosition() -xVector3(pMat->m[3][0],pMat->m[3][1],pMat->m[3][2]);	    
		float dis = Length(disvec);

		 if(IsVisual()&&m_pMesh.isNull()==false)
		 {
			 int submesh = m_pMesh->GetSubMeshCount();
			 if(m_MaterialVertex.size()<submesh)
				 m_MaterialVertex.resize(submesh);

			 for(int i = 0;i<submesh;++i)
			 {
				 if(m_MaterialVertex[i].isNull())
				 {
					 m_MaterialVertex[i] =DefaultMaterial;
				 }
				

                 xRenderScheme	* pRenderScheme = m_MaterialVertex[i]->GetRenderSchemeByDistance(dis);
				 if(pRenderScheme==NULL)
					 continue;
				 xShaderPtr  pShader =  pRenderScheme->GetShader();
				 xTechnique* pTech =   pShader->GetActiveTechnique();
				 if(pTech==NULL)
					 continue;
				 int passCount =  pTech->GetPassCount();

				 for(int j = 0;j<passCount;++j)
				 {

					 xRenderEntry* pEntry = pRenderQueue->GetNextRenderEntry();
					 if(pEntry==NULL)
						 continue;
					 pEntry->m_MeshHandle = m_pMesh->GetHandle();
					 pEntry->m_PassIndex = j;
					 pEntry->m_pRenderScheme = pRenderScheme;
					 pEntry->m_pRenderObject = this;
					 pEntry->m_ShaderHandle = pShader->GetHandle();
					 pEntry->m_SubMeshIndex = i;
					 pEntry->m_TechniqueIndex = pEntry->m_pRenderScheme->GetShader()->GetActiveTechniuqeIndex();

				 }


			 }

		 }


	    xRenderObjectVector::iterator it = m_TagObjectVector.begin();
		 xRenderObjectVector::iterator itend = m_TagObjectVector.end();
		 for(;it!=itend;++it)
		 {
			 if(*it==NULL||!(*it)->IsVisual())
				 return ;

			(*it)->_updateRenderQueue(pRenderQueue,pCamera);

		 }

		 
		 return ;
	 }



	 bool xEntity3D::AttachTagPoint
		 (const xString& boneName,xRenderObject* pObject,const xVector3& pos,const xQuaternion& quate)
	 {

		 if(pObject==NULL)
			 return false;
		 if(HasTagPoint(pObject))
			 return false;		 

		 if(pObject->GetAttachSceneNode()||pObject->GetParentNode())
		 {
			 xLogMessager::getSingleton().logMessage("attach tag point Fail ,the Object may be\
													Has Scene node ,or has parent node, name is "+pObject->GetName());
			 return false;
		 }

          if(m_pSkeleton.isNull())
		  {
			  xLogMessager::getSingleton().logMessage("attach tag point Fail ,the Object has not skeleton name is "\
				  +pObject->GetName());

			  return false;
		  }

		  xBone* pBone =  m_pSkeleton->FindBone(boneName);
		  if(pBone==NULL)
		  {
			  xLogMessager::getSingleton().logMessage("attach tag point Fail ,the skeleton has not tag point bone name is "\
				  +boneName);
			  return  false;
		  }


		 pObject->SetParentNode(pBone,this);
		 m_TagObjectVector.push_back(pObject);

		  return  true;


	 }



	 bool  xEntity3D::HasTagPoint(xRenderObject* pObject)
	 {
		 if(pObject==NULL)
			 return false;
		 xRenderObjectVector::iterator it = std::find( m_TagObjectVector.begin(),m_TagObjectVector.end(),pObject);
		 if(it==m_TagObjectVector.end())
			 return false;
		 return true;

	 }


	 bool xEntity3D::DetachTagPoint(xRenderObject* pObject)
	 {
		 if(pObject==NULL)
			 return false;
		  xRenderObjectVector::iterator it = std::find( m_TagObjectVector.begin(),m_TagObjectVector.end(),pObject);
		  if(it==m_TagObjectVector.end())
			  return false;

		 pObject->SetParentNode(NULL,NULL);
		 m_TagObjectVector.erase(it);
		 return true;
	 }


	 bool xEntity3D::HasAnimation()const
	 {
		 if(m_pSkeleton.isNull()==false)
			 return true;

		 return false;
	 }



	 void  xEntity3D::AddTime(float time)
	 {
		 return ;
	 }

	 void xEntity3D::SetAnimationSetCollect(xAnimationSetCollect* pAniCollect)
	 {
		 m_pAniationSetCollect = pAniCollect;
		 updataSkeletonAndAnimationset();
		
	 }

	 xAnimationSetCollect* xEntity3D::GetAnimationSetCollect() const 
	 {
		 
		 return m_pAniationSetCollect;
	 }


	 HRESULT xEntity3D::updataSkeletonAndAnimationset()
	 {
		 if(m_pSkeleton.isNull()|| m_pAniationSetCollect==NULL||m_pMesh.isNull()||m_pMesh->GetSkin()==NULL)
			 return X_FAIL;


		 if(m_pSkeleton->GetBoneCount()<m_pMesh->GetSkin()->GetBoneCount())
			 return X_FAIL;

		if(FAILED( m_pSkeleton->MapBoneIndex(m_pMesh->GetSkin())))
			return X_FAIL;

		 size_t size =   m_pAniationSetCollect->GetAnimationSetCount();
		 for(size_t i = 0;i<size;++i)
		 {
			 xAnimationSet* pAniSet = m_pAniationSetCollect->GetAnimationSet((UINT)i);

			 size_t anicount =pAniSet->GetAnimationCount();
			 for(size_t i = 0;i<anicount;++i)
			 {
				 xNodeAnimation* pani = pAniSet->GetNodeAniamtionByIndex((UINT)i);
				 if(pani==NULL)
					 continue;
				 xNode* pNode =  m_pSkeleton->GetBoneByName(pani->GetNodeName());
				 if(pNode==NULL)
				 {
					 xLogMessager::getSingleton().logMessage(xString("map AnimatinSet skeleton conat find bone")+pani->GetNodeName());

				 }  
				 pani->SetNode(pNode);
			 }


		 }


		 return X_OK;

	 }



	 const  xBox* xEntity3D::GetLocalBox()const
	 {
		 if(m_pMesh.isNull())
			 return NULL;
		 return m_pMesh->GetLoaclBox();

	 }

	 float xEntity3D::GetRadius() const
	 {
		 if(m_pMesh.isNull())
			 return NULL;
		 return m_pMesh->GetRadius();

	 }


	const xBox*  xEntity3D::GetWorldBox()  
	{
		if(GetLocalBox()==NULL)
			return NULL;
		m_worldbox = *(GetLocalBox());
		m_worldbox.TransForm(*GetWorldMatrix());
		return &m_worldbox;
	}


//--------------------------------------------------------


	 //-----------------------------------------------


	const  xString  xEnity3DFactory::RenderType="Enity3D";
	xRenderObject* xEnity3DFactory::CreateRenderObject(const  xRenderObjectCreateParameter& para,xSceneManager* pSceneMrg)
	{

		const xEnity3DCreateParameter* p3d =   dynamic_cast<const xEnity3DCreateParameter*>( &para);
		if (NULL == p3d)
		{
			xLogMessager::getSingleton().logMessage("create Enity3d Fail ,this parameter is err");
			return NULL;
		}

		return new xEntity3D(p3d->GetName(),pSceneMrg,p3d->m_MeshName/*,p3d->m_MaterialName*/);
	}



}